package com.zp.ojcodesandbox;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.ArrayUtil;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.*;
import com.github.dockerjava.api.model.*;
import com.github.dockerjava.core.DockerClientBuilder;
import com.zp.ojcodesandbox.model.ExecuteCodeRequest;
import com.zp.ojcodesandbox.model.ExecuteCodeSandResponse;
import com.zp.ojcodesandbox.model.ExecuteMessage;
import com.zp.ojcodesandbox.model.JudgeInfo;
import com.zp.ojcodesandbox.util.InitEnvirment;
import com.zp.ojcodesandbox.util.ProcessRunAndMessage;
import org.springframework.stereotype.Component;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

@Component
public class DockerCodeSandBoxOld implements CodeSandBox{

    private static final String Global_Code_Dir_Name = "tmpCode";
    private static final String Define_Java_class_name = "Main.java";


    public static void main(String[] args){
       // DockerCodeSandBoxOld javaNativeCodeSandBox = new DockerCodeSandBoxOld();
        JavaNativeCodeSandBox javaNativeCodeSandBox = new JavaNativeCodeSandBox();
       ExecuteCodeRequest executeCodeRequest = new ExecuteCodeRequest();
        executeCodeRequest.setInputs(Arrays.asList("1 2", "3 4"));
        String code = ResourceUtil.readStr("testCode/Main.java", StandardCharsets.UTF_8);
        executeCodeRequest.setCode(code);
        executeCodeRequest.setLanguage("java");
        ExecuteCodeSandResponse executeCodeSandResponse = javaNativeCodeSandBox.executeCode(executeCodeRequest);
        System.out.println(executeCodeSandResponse);
    }

    /**
     *
     * @param executeCodeRequest
     * @return
     */
    @Override
    public ExecuteCodeSandResponse executeCode(ExecuteCodeRequest executeCodeRequest) {
        List<String> inputs = executeCodeRequest.getInputs();
        String code = executeCodeRequest.getCode();
        String language = executeCodeRequest.getLanguage();
        // 搜索具有指定名称的公共属性
        String userDir = System.getProperty("user.dir");
        // 使用File.separator文件分隔符提高适用性
        String globalCodePathName = userDir + File.separator + Global_Code_Dir_Name;
        // 不存在文件就创建
        if(!FileUtil.exist(globalCodePathName)){
            FileUtil.mkdir(globalCodePathName);
        }
        // 将用户代码保存成文件
        String userCodeDir = globalCodePathName + File.separator + UUID.randomUUID();
        String userCodePath = userCodeDir + File.separator + Define_Java_class_name;
        File userCodeFile = FileUtil.writeString(code, userCodePath, StandardCharsets.UTF_8);

        // docker 运行程序
        DockerClient dockerClient = DockerClientBuilder.getInstance().build();
        /**
         * 创建容器并将java文件放入
         */
        // 初始化环境
        // InitEnvirment.exec(dockerClient);
        // 创建容器
        String image = InitEnvirment.JAVA_IMAGE;
        CreateContainerCmd createContainerCmd = dockerClient.createContainerCmd(image);

        HostConfig hostConfig = new HostConfig();
        // hostConfig.setBinds(new Bind(userCodeDir, new Volume("/app")));
        hostConfig.setBinds(new Bind(userCodeDir, new Volume("/app")));
        hostConfig.withMemorySwap(0L);
        hostConfig.withMemory(100 * 1000 * 1000L);
        hostConfig.withCpuCount(1L);
        // hostConfig.withSecurityOpts(Arrays.asList("seccomp=安全管理字符串")); //linux自带的安全管理
        // 设置可以交互终端 非常重要
        CreateContainerResponse createContainerResponse = createContainerCmd
                .withNetworkDisabled(true)  // 限制网络
                .withHostConfig(hostConfig)
                .withAttachStdin(true)
                .withAttachStderr(true)
                .withAttachStdout(true)
                .withTty(true)  // 很重要模式设置成可交互模式
                .withReadonlyRootfs(true) // 不能对根目录写文件
                .exec();
        String containerId = createContainerResponse.getId();
        // 启动容器
        // 编译
        dockerClient.startContainerCmd(containerId).exec();
        String[] compiledCmd = ArrayUtil.append(new String[]{"javac", "-encoding", "utf-8", "app" + File.separator + "Main.java"});
        ExecuteMessage executeMessage = ProcessRunAndMessage.dockerExecute(dockerClient, compiledCmd, "编译", containerId, false);
        List<ExecuteMessage> executeMessageList = new ArrayList<>();
        //编译无问题执行下一步
        if(executeMessage.getErrorMessage() == null || executeMessage.getErrorMessage().isEmpty()){
            // 执行
            //java -cp app Main 1 3
            for(String input: inputs){
                String[] inputArgs = input.split(" ");
                String[] runCmd = ArrayUtil.append(new String[]{"java", "-cp", "app", "Main"}, inputArgs);
                executeMessage = ProcessRunAndMessage.dockerExecute(dockerClient, runCmd, "运行", containerId, false);
                executeMessageList.add(executeMessage);
            }
        }else {
            return  null;
        }

        // 删除容器
//        if(createContainerResponse != null){
//            dockerClient.stopContainerCmd(containerId).exec();
//            dockerClient.removeContainerCmd(containerId).exec();
//        }

        // 文件清理
        if(userCodeFile.getAbsoluteFile() != null){
            Boolean delete = FileUtil.del(userCodeFile.getParentFile());
            System.out.println("delete:" + (delete ?"成功":"失败"));
        }
        ExecuteCodeSandResponse executeCodeSandResponse = new ExecuteCodeSandResponse();
        executeCodeSandResponse.setLanguage(executeCodeRequest.getLanguage());
        
        return executeCodeSandResponse;
    }

    /**
     * 错误处理方法 返回上述代码无法处理的错误信息
     * @param
     * @param executeCodeRequest
     * @return
     */
    private ExecuteCodeSandResponse getErrorResponse(Throwable e, ExecuteCodeRequest executeCodeRequest){
        ExecuteCodeSandResponse executeCodeSandResponse = new ExecuteCodeSandResponse();
        executeCodeSandResponse.setOutputs(new ArrayList<>());
        executeCodeSandResponse.setMessage(e.getMessage());
        executeCodeSandResponse.setStatus(2);
        executeCodeSandResponse.setLanguage(executeCodeRequest.getLanguage());
        executeCodeSandResponse.setJudgeInfo(new JudgeInfo());
        return executeCodeSandResponse;
    }
}
